/**
 * VYPe11 Project
 * VYPe11 language compiler.
 * Authors: Jozef Lang (xlangj01), 
 * 			Zoltan Zemko (xzemko01)
 * 2011
 */
package cz.vutbr.fit.VYPe.team18.VYPe11.sematic;

import cz.vutbr.fit.VYPe.team18.VYPe11.exception.SematicException;
import cz.vutbr.fit.VYPe.team18.VYPe11.nodes.*;
import cz.vutbr.fit.VYPe.team18.VYPe11.nodes.data_type.DataType;
import cz.vutbr.fit.VYPe.team18.VYPe11.nodes.expression.ExpressionNode;
import cz.vutbr.fit.VYPe.team18.VYPe11.nodes.function.*;
import cz.vutbr.fit.VYPe.team18.VYPe11.nodes.statement.*;
import cz.vutbr.fit.VYPe.team18.VYPe11.sematic.declared.*;

public class TreeWalker {
	public void analyze(GlobalItemListNode globalItems) throws SematicException {
		for (GlobalItemNode item : globalItems) {
			evaluate(item);
		}
		globalItems.getDeclarationLevels().verifyDeclaredFunctions();
	}
	
	private void evaluate(GlobalItemNode item) throws SematicException
	{
		if(item instanceof FunctionDefinitionNode) {
			FunctionDefinitionNode fdn=(FunctionDefinitionNode)item;
			Function fd=new Function(fdn);
			Function found=fdn.getDeclarationLevels().findFunction(fd);
			if(found!=null) {
				if(found.getIsDefined())
					throw new SematicException("Function "+found.getId()+"() was two times defined.\n");
				else
					found.setIsDefined(true);
			}
			fdn.getDeclarationLevels().addDeclaredItem(fd);
			evaluate(fdn.getStatements());
			
		}
		else {
			////System.out.print("function declaration\n");
			FunctionDeclarationNode fdn=(FunctionDeclarationNode)item;
			Function fd=new Function(fdn);
			Function found=fdn.getDeclarationLevels().findFunction(fd);
			if(found!=null) {
				throw new SematicException("Function "+found.getId()+"() was declared before.\n");
			}
			////System.out.print("function declaration added\n");
			fdn.getDeclarationLevels().addDeclaredItem(fd);
		}
	}
	
	private void evaluate(StatementListNode statements) throws SematicException
	{
		////System.out.print("-statements number: "+ statements.size() +"\n");
		if(statements.size()==0)
			return;			
			
		statements.getDeclarationLevels().levelUp();
		for (StatementNode statementNode : statements) {
			evaluate(statementNode);
			
		}
		statements.getDeclarationLevels().levelDown();
	}
	
	private void evaluate(StatementNode statement) throws SematicException
	{
		//System.out.print(statement.toString()+"\n");
		if(statement instanceof AssigmentStatementNode)
		{
			AssigmentStatementNode as=(AssigmentStatementNode)statement;
			Variable var=as.getDeclarationLevels().findVariableById(
									as.getIdentifier().getId()
					);
			DataType vdt=var.getType();
			DataType edt=as.getExpression().getType();
			if(var.getType()!=edt
				&& !(vdt==DataType.INT && edt==DataType.SHORT))
			{
				throw new SematicException("Assignment: Value \""+var.getId()+"\" has different type than expression");
			}		
		}
		else if(statement instanceof BreakStatementNode)
		{
			// ...
		}
		else if(statement instanceof ContinueStatementNode)
		{
			// ...
		}
		else if(statement instanceof ExpressionStatementNode)
		{
			ExpressionStatementNode es=(ExpressionStatementNode)statement;
			es.getExpression().getType(); // kvoli kontrole typov
		}
		else if(statement instanceof IfElseStatementNode)
		{
			IfElseStatementNode ifs=(IfElseStatementNode)statement;
			
			// overenie typu vyrazu v if podmienke
			DataType dt=ifs.getExpression().getType();
			if(dt!=DataType.SHORT && dt!=DataType.INT)
				throw new SematicException("If condition need to be numerical value.");
			evaluate(ifs.getIfStatements());
			evaluate(ifs.getElseStatements());
		}
		else if(statement instanceof ReturnStatementNode)
		{
			Function fd=(Function)statement.getDeclarationLevels().getFirst().getLast();
			DataType fdt=fd.getReturnType();
			
			ExpressionNode node=((ReturnStatementNode)statement).getExpression();
			DataType edt;
			if(node!=null)
				edt=((ReturnStatementNode)statement).getExpression().getType();
			else
				edt=DataType.VOID;				
			
			if(edt!=fdt && !(fdt==DataType.INT && edt==DataType.SHORT))
					throw new SematicException(
							"Expression type can not converted to the " +
							fd.getId()+"() function return type."
							);
		}
		else if(statement instanceof VariableDeclarationStatementNode)
		{
			statement.getDeclarationLevels().addVariablesByStatement(
									(VariableDeclarationStatementNode)statement);
		}
		else if(statement instanceof WhileStatementNode)
		{
			WhileStatementNode ws=(WhileStatementNode)statement;
			
			// overenie typu vyrazu v while podmienke
			DataType dt=ws.getExpression().getType();
			if(dt!=DataType.SHORT && dt!=DataType.INT)
				throw new SematicException(
						"While condition need to be numerical value.");
			evaluate(ws.getStatements());
		}
		else
		{
			throw new SematicException("Unknow statement node.");
		}
	}
}
